SPDX-FileCopyrightText: 2022 Mario Hidalgo Venegas & Lucas Van Melderen SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
18/01/2023 Blender version 3.3.0
#
Mario Hidalgo & Lucas Van Melderen - Arvo Pärt : Spiegel im Spiegel # # Python Code # # AIM 2022-23 / AlICe lab / Final Review 19.01 # #
Here are the general definitions and inputs, the code begin at line : 70
import bpy, random, bmeshdef Clean():
RemoveAll = bpy.context.copy()
RemoveAll["selected_objects"] = list(bpy.context.scene.objects)
bpy.ops.object.delete(RemoveAll)def Deselect_All():
bpy.ops.object.select_all(action="DESELECT")def Select_obj(Selected):
bpy.data.objects[Selected].select_set(state=True)def Select_and_Active_obj(Actived):
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = bpy.context.view_layer.objects[Actived]
bpy.context.active_object.select_set(state=True)def Edit_Mode():
bpy.ops.object.editmode_toggle()def New_loc(obj_loc, lx, ly, lz):
bpy.data.objects[obj_loc].location.x = lx
bpy.data.objects[obj_loc].location.y = ly
bpy.data.objects[obj_loc].location.z = lzdef Resize(rx, ry, rz):
bpy.ops.transform.resize(value=(rx, ry, rz))def Translate(tx, ty, tz):
bpy.ops.transform.translate(value=(tx, ty, tz))def Extrude_malifold(ext_dim):
Edit_Mode()
bpy.ops.mesh.extrude_manifold(
MESH_OT_extrude_region={
"use_normal_flip": False,
"use_dissolve_ortho_edges": True,
"mirror": False,
},
TRANSFORM_OT_translate={
"value": (0, -1.19209e-07, ext_dim),
"orient_axis_ortho": "X",
"orient_type": "GLOBAL",
"orient_matrix": (
(9.30786e-06, -0.99797, -0.0636786),
(0.987873, -0.0098779, 0.154951),
(-0.155265, -0.0629079, 0.985868),
),
"orient_matrix_type": "NORMAL",
"constraint_axis": (True, True, True),
"mirror": False,
"use_proportional_edit": False,
"proportional_edit_falloff": "SMOOTH",
"proportional_size": 1,
"use_proportional_connected": False,
"use_proportional_projected": False,
"snap": False,
"snap_elements": {"INCREMENT"},
"use_snap_project": False,
"snap_target": "CLOSEST",
"use_snap_self": True,
"use_snap_edit": True,
"use_snap_nonedit": True,
"use_snap_selectable_only": False,
"use_snap_to_same_target": False,
"snap_face_nearest_steps": 1,
"snap_point": (0, 0, 0),
"snap_align": False,
"snap_normal": (0, 0, 0),
"gpencil_strokes": False,
"cursor_transform": False,
"texture_space": False,
"remove_on_cancel": False,
"view2d_edge_pan": False,
"release_confirm": True,
"use_accurate": False,
"use_automerge_and_split": True,
},
)
Edit_Mode()def Reset_origin():
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
obj = bpy.data.objects
mesh = bpy.data.meshes
Coll_obj = bpy.context.scene.collection.objects #####
Active_obj = bpy.context.view_layer.objects
Delete = bpy.ops.mesh.delete #
C O D E #
#
Clean()Create the base mesh : ” WAVE ” #
def create_WAVE():
bpy.ops.mesh.primitive_grid_add(
size=84, x_subdivisions=6, y_subdivisions=84, location=(0, 42, 0)
)
Resize(1 / 14, 1, 1)Using the 1/14 ratio in order to create a grid of 84x6 units (from the 84x84’s one)
Reset_origin()
obj["Grid"].name = "WAVE"
grid = bpy.context.selected_objects[0]
vertices = list(grid.data.vertices)Translating verts coord. to the “field” dimensions of the analysis (84x44 units) Multiplying each added dimensions (+2,-2,+15,-15,+2,-2) by 14 to match with the vector’s resized grid system
for v in vertices:
if v.co.x > 0.5:
v.co.x += 2 * 14
for v in vertices:
if v.co.x < -0.5:
v.co.x -= 2 * 14 # | | | | | | |
for v in vertices: # From : |1|1|1|1|1|1|
if v.co.x > 3 * 14: # | | | | | | |
v.co.x += 15 * 14
for v in vertices:
if (
v.co.x < -3 * 14
): # | | | | | | |
v.co.x -= (
15 * 14
) # to : | 3 | 16 | 3 | 3 | 16 | 3 |
for v in vertices: # | | | | | | |
if v.co.x > 19 * 14:
v.co.x += 2 * 14
for v in vertices:
if v.co.x < -19 * 14:
v.co.x -= 2 * 14Sorting each X coord representative of a mirror action with the Y coords Then, allocating the step value (Z coord).
def MirrorUpper_A(YCo, Step):
for v in vertices:
if int(v.co.x) == 3 * 14 and int(v.co.y) in YCo:
v.co.z = Step def MirrorUpper_B(YCo, Step):
for v in vertices:
if int(v.co.x) == 19 * 14 and int(v.co.y) in YCo:
v.co.z = Step def MirrorLower_A(YCo, Step):
for v in vertices:
if int(v.co.x) == -3 * 14 and int(v.co.y) in YCo:
v.co.z = Step def MirrorLower_B(YCo, Step):
for v in vertices:
if int(v.co.x) == -19 * 14 and int(v.co.y) in YCo:
v.co.z = Step MirrorLower_A([3], -1)
MirrorUpper_B([3], 1)
MirrorLower_B([10, 11], -2)
MirrorUpper_A([10, 11], 2)
MirrorLower_A([18, 19, 20], -3)
MirrorUpper_B([18, 19, 20], 3)
MirrorLower_B([27, 28, 29, 30], -4)
MirrorUpper_A([27, 28, 29, 30], 4)
MirrorLower_A([37, 38, 39, 40, 41], -5)
MirrorUpper_B([37, 38, 39, 40, 41], 5)
MirrorLower_B([48, 49, 50, 51, 52, 53], -6)
MirrorUpper_A([48, 49, 50, 51, 52, 53], 6)
MirrorLower_A([60, 61, 62, 63, 64, 65, 66], -7)
MirrorUpper_B([60, 61, 62, 63, 64, 65, 66], 7)
MirrorLower_B([73, 74, 75, 76, 77, 78, 79, 80], -8)
MirrorUpper_A([73, 74, 75, 76, 77, 78, 79, 80], 8)Applying the definition for the selected verts (analysis based)
create_WAVE()Giving some thighness and smoothness to the Wave’s mesh with modifiers
Select_and_Active_obj("WAVE")
mod_add = bpy.ops.object.modifier_add
mod_app = bpy.ops.object.modifier_apply
mod = bpy.context.object.modifiers
mod_add(type="SOLIDIFY")
mod["Solidify"].thickness = 0.5
mod["Solidify"].offset = 0
mod_app(modifier="Solidify")
mod_add(type="SUBSURF")
mod["Subdivision"].levels = 3
mod_app(modifier="Subdivision")Generate the “CLOUDS” based on the Wave’s mesh #
Calling again the same definition to generate the starting mesh of the “clouds”
create_WAVE()
obj["WAVE.001"].name = "CLOUDS"
Select_and_Active_obj("CLOUDS")Applying different modifiers to generate a ew “noised” mesh which would surround the first one with variations (analysis based)
mod_add = bpy.ops.object.modifier_add
mod = bpy.context.object.modifiers
mod_add(type="DECIMATE")
mod["Decimate"].decimate_type = "UNSUBDIV"
mod["Decimate"].iterations = 1Adding texture to the displace modifiers to achieve the desired result
bpy.ops.texture.new()
tex = bpy.data.textures["Texture"]
tex.type = "CLOUDS"
tex.noise_basis = "VORONOI_F2"
tex.noise_scale = 10
tex.noise_depth = 0
tex.nabla = 0.1
tex.intensity = 0.4
tex.contrast = 2.9
tex.saturation = 0
mod_add(type="DISPLACE")
mod["Displace"].texture = tex
mod["Displace"].texture_coords = "GLOBAL"
mod["Displace"].direction = "NORMAL"
mod["Displace"].strength = 8Extruding and Resizing it in order to fit with Wave’s bondaries
Extrude_malifold(5.5)
Resize(1.2, 1.1, 1.5)
Translate(-1, 0, -6)
mod_add(type="SUBSURF")
mod["Subdivision"].levels = 3Create the 3D grid #
Using loops and ranges to create a 3D grid mesh (the cubic fragment only)
def Grid_of_points(rows, columns, levels):
mesh_grid = mesh.new("Mesh_Grid")
obj_grid = obj.new("GRID", mesh_grid)
verts, edges = [], []
i = 0
for l in range(levels):
for r in range(rows):
for c in range(columns):
verts.append((c, r, l))
if c < columns - 1:
edges.append((i, i + 1))
if r < rows - 1:
edges.append((i, i + columns))
if l < levels - 1:
edges.append((i, i + (rows * columns)))
i += 1
mesh_grid.from_pydata(verts, edges, [])
Coll_obj.link(obj_grid)
obj_grid.name = "GRID"
Select_and_Active_obj("GRID")
Edit_Mode()
Delete(type="EDGE_FACE") # Deleting the edges and faces to keep the vertices only
Edit_Mode()
Grid_of_points(20, 20, 20)Specifying the limits for the random cube’s location to always end up inside of the whole score
Fragment_x = random.randint(-22, 3) #
Fragment_y = random.randint(0, 65)
Horizon_z = -9.5z = -10(+0.5) -> Adding +0.5 because the crosses’s cursors will be in the center of the geometry (and not at the bottom)
New_loc("GRID", Fragment_x, Fragment_y, Horizon_z)Relocating the grid to the randomly selected fragment
SELECTION IS INSIDE #
Select_and_Active_obj("GRID")Identifying which points of the GRID are inside the CLOUDS with a definition based on the fonction “closest point on a mesh” and on a max distance (from it)
def is_inside(points_B, max_dist, obj_A):
result, points_A, normal, face = obj_A.closest_point_on_mesh(
points_B, distance=max_dist
)
if not result:
return False
points_C = points_A - points_B
v = points_C.dot(normal)
return not (v < 0.0)
obj_A = obj["GRID"]
obj_B = obj["CLOUDS"]
Edit_Mode()
mesh = bmesh.from_edit_mesh(bpy.context.object.data)
obj_B_matrix = obj_B.matrix_world.inverted()
mat = obj_B_matrix @ obj_A.matrix_world
for v in mesh.verts:
points_B = mat @ v.co
if is_inside(points_B, 20, obj_B):
v.select = TrueDeleting the selected points/verts
Delete(type="VERT")
Edit_Mode()
Deselect_All()Instancing CROSS #
Creating 1x1x1 cross from individual’s extrusion of each faces of a cube
bpy.ops.mesh.primitive_cube_add(scale=(0.1, 0.1, 0.1))
Edit_Mode()
bpy.ops.mesh.extrude_faces_move(TRANSFORM_OT_shrink_fatten={"value": 0.4})
Edit_Mode()
obj["Cube"].name = "CROSS"Select_and_Active_obj("GRID")
Select_obj("CROSS")
bpy.ops.object.parent_set(type="OBJECT")
bpy.context.object.instance_type = "VERTS"Relocating the crosses to the same origin as the grid
New_loc("CROSS", Fragment_x, Fragment_y, Horizon_z)Boolean Cube #
Cutting the “Wave’s” in the limits of the randomly selected fragment
bpy.ops.mesh.primitive_cube_add(size=20)
New_loc(
"Cube", Fragment_x + 9.5, Fragment_y + 9.5, 0
) # +9.5 because cube's origin is in the center of the geometry
Select_and_Active_obj("WAVE")
mod_add = bpy.ops.object.modifier_add
mod_app = bpy.ops.object.modifier_apply
mod = bpy.context.object.modifiers
mod_add(type="BOOLEAN")
mod["Boolean"].object = obj["Cube"]
mod["Boolean"].solver = "EXACT"
mod["Boolean"].operation = "INTERSECT"
mod_app(modifier="Boolean")Hiding useless objects #
bpy.data.objects["CLOUDS"].hide_set(True)
bpy.data.objects["Cube"].hide_set(True)
Deselect_All()Printing console #
print("---------------------------------------------")
print(" ")
print(" ")
print(" Generated fragment location :")
print(" ")
print(" x =", Fragment_x + 9.5)
print(" Y =", Fragment_y + 9.5)
print(" ")
Amplitude = obj["WAVE"].dimensions.z
print(" Wave Amplitude = ", round(Amplitude))
print(" ")
Select_and_Active_obj("GRID")
Edit_Mode()
mesh = bpy.context.view_layer.objects.active.data
bm = bmesh.from_edit_mesh(mesh)
print(" Cross amount =", len(bm.verts))
print(" -> Density =", len(bm.verts) / 8000)
Edit_Mode()
Deselect_All()
print(" ")
print(" ")